/**
 * Universidad Del Valle De Guatemala.
 * Algoritmos y Estructuras de Datos.
 * Sección 20.
 * Miguel Enrique Zea Arenales.
 * Carné 09238.
 * Hoja de Trabajo 8.
 * 08/11/2012
 */
package shortestpath;

import java.io.*;
import java.util.ArrayList;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 * Aplicación principal.
 */
public class SearchPathCities extends javax.swing.JFrame {
    String rawData = "", GraphCenter; // Información del archivo de texto, centro de grafo.
    DirectedGraphMatrix Cities = new DirectedGraphMatrix(50);
    /**
     * Creates new form SearchPathCities
     */
    public SearchPathCities() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        FileChooser = new javax.swing.JFileChooser();
        jScrollPane1 = new javax.swing.JScrollPane();
        Console = new javax.swing.JTextArea();
        wCity = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        addEntry = new javax.swing.JButton();
        saveFile = new javax.swing.JButton();
        openFile = new javax.swing.JButton();
        calcShortestRoute = new javax.swing.JButton();
        calcGraphCenter = new javax.swing.JButton();
        showAdjMatrix = new javax.swing.JButton();
        destCity = new javax.swing.JTextField();
        origCity = new javax.swing.JTextField();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        Console.setColumns(20);
        Console.setRows(5);
        jScrollPane1.setViewportView(Console);

        jLabel1.setText("Ciudad Origen");

        jLabel2.setText("Ciudad Destino");

        jLabel3.setText("Distancia");

        addEntry.setText("Agregar/Modificar Entrada");
        addEntry.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addEntryActionPerformed(evt);
            }
        });

        saveFile.setText("Guardar Archivo");
        saveFile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveFileActionPerformed(evt);
            }
        });

        openFile.setText("Abrir Archivo");
        openFile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                openFileActionPerformed(evt);
            }
        });

        calcShortestRoute.setText("Calcular Ruta y Distancia más Corta");
        calcShortestRoute.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                calcShortestRouteActionPerformed(evt);
            }
        });

        calcGraphCenter.setText("Calcular Centro de Grafo");
        calcGraphCenter.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                calcGraphCenterActionPerformed(evt);
            }
        });

        showAdjMatrix.setText("Desplegar Matriz de Adyacencia");
        showAdjMatrix.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showAdjMatrixActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 402, Short.MAX_VALUE)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(calcShortestRoute)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(calcGraphCenter, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addGroup(layout.createSequentialGroup()
                            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addComponent(jLabel1)
                                .addComponent(origCity, javax.swing.GroupLayout.PREFERRED_SIZE, 135, javax.swing.GroupLayout.PREFERRED_SIZE))
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(layout.createSequentialGroup()
                                    .addComponent(jLabel2)
                                    .addGap(71, 71, 71)
                                    .addComponent(jLabel3)
                                    .addGap(0, 0, Short.MAX_VALUE))
                                .addGroup(layout.createSequentialGroup()
                                    .addComponent(destCity, javax.swing.GroupLayout.PREFERRED_SIZE, 135, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(wCity))))
                        .addComponent(showAdjMatrix)
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(addEntry)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(saveFile)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(openFile, javax.swing.GroupLayout.DEFAULT_SIZE, 120, Short.MAX_VALUE))))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 165, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2)
                    .addComponent(jLabel3))
                .addGap(5, 5, 5)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(wCity, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(destCity, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(origCity, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(addEntry)
                    .addComponent(saveFile)
                    .addComponent(openFile))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(calcShortestRoute)
                    .addComponent(calcGraphCenter))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(showAdjMatrix)
                .addContainerGap(17, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    /**
     * Pre: se abrió o modificó recientemente el archivo fuente de datos para
     * el grafo.
     * Post: se rescribe el grafo y su matriz de adyacencia reflejando los 
     * cambios a los datos.
     */
    private void updateCityData() {
        String[] lines, words;
        
        Cities = new DirectedGraphMatrix(50);
        
        lines = rawData.split("\n");
        for(int i = 0; i < lines.length; i++) {
            words = lines[i].split(" ");
            // Origen
            if((Cities.getVertices().isEmpty()) || (!Cities.getVertices().contains(words[0]))) {
                Cities.addVertex(words[0]);
            } 
            // Destino
            if(!Cities.getVertices().contains(words[1])) {
                Cities.addVertex(words[1]);
            }
            // Peso
            Cities.addEdge(words[0], words[1], Integer.parseInt(words[2]));
        }
    }
    
    /**
     * Pre: debe existir un grafo con una matriz de adyacencia asociada.
     * Post: se genera el estado incial de la matriz de rutas correspondiente
     * al algoritmo de Floyd.
     */
    private int[][] initPathMatrix(int[][] adjMatrix) {
        int i, j, n;
        n = adjMatrix.length;
        int[][] path = new int[n][n];
        
        for(i = 0; i < n; i++) {
            for(j = 0; j < n; j++) {
                if(adjMatrix[i][j] == 999999) {
                    path[i][j] = -1;
                } else {
                    path[i][j] = i;
                }
            }
        }
        
        for(i = 0; i < n; i++) {
            path[i][i] = i;
        }
        return path;
    }
    
    /**
     * Pre: debe preseionarse el botón de abrir archivo.
     * Post: se abre un dialogo de búsqueda de archivos para localizar los datos
     * fuente para el grafo.
     */
    private void openFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openFileActionPerformed
        String file_name = "write.txt";
        int returnVal = FileChooser.showOpenDialog(this);
        if (returnVal == javax.swing.JFileChooser.APPROVE_OPTION) {
            File file = FileChooser.getSelectedFile();
            file_name = file.toString();
        }
        
        try{
            FileInputStream fstream = new FileInputStream(file_name);
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String strLine;
            rawData = "";
            while ((strLine = br.readLine()) != null) {
                rawData = rawData + strLine + "\n";
            }
            in.close();
            Console.append(rawData);
        } catch (Exception ex) {
            System.err.println("Error: " + ex.getMessage());
        }
        
        updateCityData();
    }//GEN-LAST:event_openFileActionPerformed

    /**
     * Pre: debe haberse presionado el botón para guardar archivo.
     * Post: se guardan la información actual del grafo en un archivo de texto.
     */
    private void saveFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveFileActionPerformed
        String file_name = "write.txt";                                         // Nombre por defecto del archivo de texto a crear.
        FileNameExtensionFilter ft = new FileNameExtensionFilter("Text Files", "txt"); //Filtro para el filechooser, sólo archivos de texto.
        FileChooser.addChoosableFileFilter(ft);
        int returnVal = FileChooser.showSaveDialog(this);                     // Se muestra el filechooser.
        if (returnVal == javax.swing.JFileChooser.APPROVE_OPTION) {
            File file = FileChooser.getSelectedFile();                        // Al momento que el usuario presiona aceptar, se almacena la dirección
            file_name = file.toString();                                        // del archivo creado.
        }
        
        Writer output = null;
        File file = new File(file_name + ".txt");                               // Se crea el archivo de texto en la dirección especificada por el filechooser.
        try {
            output = new BufferedWriter(new FileWriter(file));                  // Se escribe en el archivo la cadena de números aleatorios y se cierra el mismo.
            output.write(rawData);
            output.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            Console.append("-----------Archivo creado exitosamente-----------\n");                 // Se le hace saber al usuario la creación exitosa del archivo.
        }
    }//GEN-LAST:event_saveFileActionPerformed

    /**
     * Pre: debe haberse presionado el botón de agregar entrada.
     * Post: se agrega una pareja de vértices con su arco respectivo al archivo
     * de datos actualmente abierto
     */
    private void addEntryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addEntryActionPerformed
        try {
            int test;
            test = Integer.parseInt(wCity.getText());
        } catch(NumberFormatException ex) {
            Console.append("ERROR: La distancia entre ciudades debe ser un número mayor a cero\n");
        } finally {
            if(Integer.parseInt(wCity.getText()) < 0) {
                Console.append("ERROR: La distancia entre ciudades debe ser un número mayor a cero");
            } else {
                rawData = rawData + origCity.getText() + " " + destCity.getText() + " " + wCity.getText() + "\n";
                Console.append("-----La Entrada se Añadió Correctamente-----\n");
            }
            updateCityData();
        }
    }//GEN-LAST:event_addEntryActionPerformed

    /**
     * Pre: debe haberse creado un grafo a partir de un archivo de texto fuente.
     * Post: se imprime en el área dde texto la matriz de adyacencia correspondiente
     * al grafo.
     */
    private void showAdjMatrixActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showAdjMatrixActionPerformed
        int[][] displayMatrix;
        
        displayMatrix = Cities.getAdjMat();
        for(int i = 0; i < Cities.size(); i++) {
            for(int j = 0; j < Cities.size(); j++) {
                Console.append(displayMatrix[i][j] + "\t");
            }
            Console.append("\n");
        }
        Console.append("\n");
    }//GEN-LAST:event_showAdjMatrixActionPerformed

    /**
     * Pre: debe existir un grafo a partir de los datos importados.
     * Post: se despliegan en el área de texto la distancia y la ruta más corta
     * para llegar del origen al destino indicados.
     */
    private void calcShortestRouteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_calcShortestRouteActionPerformed
        ArrayList<Integer> GeneratedPath = new ArrayList<>();
        int[][] adjMatrix, pathMatrix, cMatrix;
        int o,d;
        
        updateCityData();
        adjMatrix = Cities.getAdjMat();
        pathMatrix = initPathMatrix(adjMatrix);
        o = Cities.getVertices().indexOf(origCity.getText());
        d = Cities.getVertices().indexOf(destCity.getText());
        
        try {
            GeneratedPath = PathSearcher.FloydPathSearch(adjMatrix, pathMatrix, o, d);
            Console.append("*****El camino que debe tomarse es:\n");
            Console.append(Cities.getVertex(GeneratedPath.get(0)).toString() + " ");
            if(GeneratedPath.size() == 2) {
                
            } else if(GeneratedPath.size() == 3) {
                Console.append("-> " + Cities.getVertex(GeneratedPath.get(1)).toString() + " ");
            } else {
                for(int i = GeneratedPath.size() - 3; i >= 1; i--) {
                    Console.append("-> " + Cities.getVertex(GeneratedPath.get(i)).toString() + " ");
                }    
            }
            Console.append("-> " + Cities.getVertex(GeneratedPath.get(GeneratedPath.size() - 2)).toString() + " ");
        
            Console.append("\n*****Recorriendo una distancia total de:\n");
            Console.append("" + GeneratedPath.get(GeneratedPath.size()-1).toString() + "km\n");
        
            cMatrix = new int[Cities.size()][Cities.size()];
        
            for(int i = 0; i < Cities.size(); i++) {
                for(int j = 0; j < Cities.size(); j++) {
                    cMatrix[i][j] = adjMatrix[i][j];
                }
            }
            
            GraphCenter = Cities.getVertex(PathSearcher.GraphCenter(cMatrix));
        } catch(Exception ex) {
            Console.append("ERROR: No existe conexión entre origen y destino\n");
        } 
    }//GEN-LAST:event_calcShortestRouteActionPerformed

    /**
     * Pre: debe haberse calculado por lo menos una vez el algoritmo de Floyd.
     * Post: se despliega en el área de texto el centro del grafo actual.
     */
    private void calcGraphCenterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_calcGraphCenterActionPerformed
        Console.append("*****El Centro del Grafo es:\n" + GraphCenter + "\n");
    }//GEN-LAST:event_calcGraphCenterActionPerformed

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /*
         * Set the Nimbus look and feel
         */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /*
         * If Nimbus (introduced in Java SE 6) is not available, stay with the
         * default look and feel. For details see
         * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(SearchPathCities.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(SearchPathCities.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(SearchPathCities.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(SearchPathCities.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /*
         * Create and display the form
         */
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new SearchPathCities().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTextArea Console;
    private javax.swing.JFileChooser FileChooser;
    private javax.swing.JButton addEntry;
    private javax.swing.JButton calcGraphCenter;
    private javax.swing.JButton calcShortestRoute;
    private javax.swing.JTextField destCity;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton openFile;
    private javax.swing.JTextField origCity;
    private javax.swing.JButton saveFile;
    private javax.swing.JButton showAdjMatrix;
    private javax.swing.JTextField wCity;
    // End of variables declaration//GEN-END:variables
}
